@workglow/ai 0.0.57 → 0.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +44 -9
  2. package/dist/browser.js +89 -104
  3. package/dist/browser.js.map +13 -13
  4. package/dist/bun.js +89 -130
  5. package/dist/bun.js.map +13 -15
  6. package/dist/common.d.ts +2 -1
  7. package/dist/common.d.ts.map +1 -1
  8. package/dist/model/{storage/InMemoryModelRepository.d.ts → InMemoryModelRepository.d.ts} +2 -2
  9. package/dist/model/InMemoryModelRepository.d.ts.map +1 -0
  10. package/dist/model/ModelRegistry.d.ts +12 -0
  11. package/dist/model/ModelRegistry.d.ts.map +1 -1
  12. package/dist/model/ModelRepository.d.ts +59 -60
  13. package/dist/model/ModelRepository.d.ts.map +1 -1
  14. package/dist/model/ModelSchema.d.ts +42 -0
  15. package/dist/model/ModelSchema.d.ts.map +1 -0
  16. package/dist/node.js +89 -130
  17. package/dist/node.js.map +13 -15
  18. package/dist/provider/AiProviderRegistry.d.ts +15 -3
  19. package/dist/provider/AiProviderRegistry.d.ts.map +1 -1
  20. package/dist/task/DownloadModelTask.d.ts +179 -19
  21. package/dist/task/DownloadModelTask.d.ts.map +1 -1
  22. package/dist/task/TextEmbeddingTask.d.ts +77 -9
  23. package/dist/task/TextEmbeddingTask.d.ts.map +1 -1
  24. package/dist/task/TextGenerationTask.d.ts +77 -9
  25. package/dist/task/TextGenerationTask.d.ts.map +1 -1
  26. package/dist/task/TextQuestionAnswerTask.d.ts +77 -9
  27. package/dist/task/TextQuestionAnswerTask.d.ts.map +1 -1
  28. package/dist/task/TextRewriterTask.d.ts +77 -9
  29. package/dist/task/TextRewriterTask.d.ts.map +1 -1
  30. package/dist/task/TextSummaryTask.d.ts +77 -9
  31. package/dist/task/TextSummaryTask.d.ts.map +1 -1
  32. package/dist/task/TextTranslationTask.d.ts +77 -9
  33. package/dist/task/TextTranslationTask.d.ts.map +1 -1
  34. package/dist/task/base/AiTask.d.ts +15 -6
  35. package/dist/task/base/AiTask.d.ts.map +1 -1
  36. package/dist/task/base/AiTaskSchemas.d.ts +43 -2
  37. package/dist/task/base/AiTaskSchemas.d.ts.map +1 -1
  38. package/dist/types.d.ts +0 -4
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +9 -12
  41. package/dist/model/Model.d.ts +0 -26
  42. package/dist/model/Model.d.ts.map +0 -1
  43. package/dist/model/storage/InMemoryModelRepository.d.ts.map +0 -1
  44. package/dist/model/storage/IndexedDbModelRepository.d.ts +0 -18
  45. package/dist/model/storage/IndexedDbModelRepository.d.ts.map +0 -1
  46. package/dist/model/storage/PostgresModelRepository.d.ts +0 -19
  47. package/dist/model/storage/PostgresModelRepository.d.ts.map +0 -1
  48. package/dist/model/storage/SqliteModelRepository.d.ts +0 -18
  49. package/dist/model/storage/SqliteModelRepository.d.ts.map +0 -1
package/README.md CHANGED
@@ -32,9 +32,11 @@ import {
32
32
  setGlobalModelRepository,
33
33
  InMemoryModelRepository,
34
34
  AiJob,
35
+ AiJobInput,
35
36
  } from "@workglow/ai";
36
- import { Workflow, getTaskQueueRegistry } from "@workglow/task-graph";
37
- import { JobQueue } from "@workglow/job-queue";
37
+ import { Workflow, getTaskQueueRegistry, TaskInput, TaskOutput } from "@workglow/task-graph";
38
+ import { ConcurrencyLimiter, JobQueueClient, JobQueueServer } from "@workglow/job-queue";
39
+ import { InMemoryQueueStorage } from "@workglow/storage";
38
40
  import { HF_TRANSFORMERS_ONNX, register_HFT_InlineJobFns } from "@workglow/ai-provider";
39
41
 
40
42
  // 1. Set up a model repository
@@ -59,7 +61,23 @@ await modelRepo.connectTaskToModel("TextGenerationTask", "onnx:Xenova/LaMini-Fla
59
61
  await register_HFT_InlineJobFns();
60
62
 
61
63
  // 5. Set up job queue for the provider
62
- getTaskQueueRegistry().registerQueue(new JobQueue(HF_TRANSFORMERS_ONNX, AiJob));
64
+ const queueName = HF_TRANSFORMERS_ONNX;
65
+ const storage = new InMemoryQueueStorage<AiJobInput<TaskInput>, TaskOutput>(queueName);
66
+
67
+ const server = new JobQueueServer<AiJobInput<TaskInput>, TaskOutput>(AiJob, {
68
+ storage,
69
+ queueName,
70
+ limiter: new ConcurrencyLimiter(1),
71
+ });
72
+
73
+ const client = new JobQueueClient<AiJobInput<TaskInput>, TaskOutput>({
74
+ storage,
75
+ queueName,
76
+ });
77
+
78
+ client.attach(server);
79
+ getTaskQueueRegistry().registerQueue({ server, client, storage });
80
+ await server.start();
63
81
 
64
82
  // 6. Create and run a workflow
65
83
  const workflow = new Workflow();
@@ -122,8 +140,8 @@ import { TextTranslationTask } from "@workglow/ai";
122
140
  const task = new TextTranslationTask({
123
141
  model: "translation-model",
124
142
  text: "Hello, how are you?",
125
- sourceLanguage: "en",
126
- targetLanguage: "es",
143
+ source_lang: "en",
144
+ target_lang: "es",
127
145
  });
128
146
 
129
147
  const result = await task.run();
@@ -344,12 +362,29 @@ For compute-intensive tasks that should run in workers:
344
362
  Each provider needs a job queue for task execution:
345
363
 
346
364
  ```typescript
347
- import { getTaskQueueRegistry } from "@workglow/task-graph";
348
- import { JobQueue } from "@workglow/job-queue";
349
- import { AiJob } from "@workglow/ai";
365
+ import { getTaskQueueRegistry, TaskInput, TaskOutput } from "@workglow/task-graph";
366
+ import { ConcurrencyLimiter, JobQueueClient, JobQueueServer } from "@workglow/job-queue";
367
+ import { InMemoryQueueStorage } from "@workglow/storage";
368
+ import { AiJob, AiJobInput } from "@workglow/ai";
350
369
  import { HF_TRANSFORMERS_ONNX } from "@workglow/ai-provider";
351
370
 
352
- getTaskQueueRegistry().registerQueue(new JobQueue(HF_TRANSFORMERS_ONNX, AiJob));
371
+ const queueName = HF_TRANSFORMERS_ONNX;
372
+ const storage = new InMemoryQueueStorage<AiJobInput<TaskInput>, TaskOutput>(queueName);
373
+
374
+ const server = new JobQueueServer<AiJobInput<TaskInput>, TaskOutput>(AiJob, {
375
+ storage,
376
+ queueName,
377
+ limiter: new ConcurrencyLimiter(1),
378
+ });
379
+
380
+ const client = new JobQueueClient<AiJobInput<TaskInput>, TaskOutput>({
381
+ storage,
382
+ queueName,
383
+ });
384
+
385
+ client.attach(server);
386
+ getTaskQueueRegistry().registerQueue({ server, client, storage });
387
+ await server.start();
353
388
  ```
354
389
 
355
390
  ## Workflow Integration
package/dist/browser.js CHANGED
@@ -6,31 +6,14 @@ import {
6
6
  PermanentJobError
7
7
  } from "@workglow/job-queue";
8
8
 
9
- // src/model/storage/InMemoryModelRepository.ts
9
+ // src/model/ModelRegistry.ts
10
+ import { createServiceToken, globalServiceRegistry } from "@workglow/util";
11
+
12
+ // src/model/InMemoryModelRepository.ts
10
13
  import { InMemoryTabularRepository } from "@workglow/storage";
11
14
 
12
15
  // src/model/ModelRepository.ts
13
16
  import { EventEmitter } from "@workglow/util";
14
- var ModelSchema = {
15
- type: "object",
16
- properties: {
17
- name: { type: "string" },
18
- details: { type: "string" }
19
- },
20
- required: ["name", "details"],
21
- additionalProperties: false
22
- };
23
- var ModelPrimaryKeyNames = ["name"];
24
- var Task2ModelSchema = {
25
- type: "object",
26
- properties: {
27
- task: { type: "string" },
28
- model: { type: "string" }
29
- },
30
- required: ["task", "model"],
31
- additionalProperties: false
32
- };
33
- var Task2ModelPrimaryKeyNames = ["task", "model"];
34
17
 
35
18
  class ModelRepository {
36
19
  type = "ModelRepository";
@@ -48,101 +31,101 @@ class ModelRepository {
48
31
  return this.events.waitOn(name);
49
32
  }
50
33
  async addModel(model) {
51
- await this.modelTabularRepository.put({ name: model.name, details: JSON.stringify(model) });
52
- this.models.set(model.name, model);
34
+ await this.modelTabularRepository.put(model);
53
35
  this.events.emit("model_added", model);
54
36
  return model;
55
37
  }
56
38
  async findModelsByTask(task) {
57
39
  if (typeof task != "string")
58
40
  return;
59
- const junctions = await this.task2ModelTabularRepository.search({ task });
60
- if (!junctions || junctions.length === 0)
41
+ const allModels = await this.modelTabularRepository.getAll();
42
+ if (!allModels || allModels.length === 0)
43
+ return;
44
+ const models = allModels.filter((model) => model.tasks?.includes(task));
45
+ if (models.length === 0)
61
46
  return;
62
- const models = [];
63
- for (const junction of junctions) {
64
- const model = await this.modelTabularRepository.get({ name: junction.model });
65
- if (model)
66
- models.push(JSON.parse(model.details));
67
- }
68
- models.forEach((m) => this.models.set(m.name, m));
69
- this.taskModels.set(task, models);
70
47
  return models;
71
48
  }
72
- models = new Map;
73
- taskModels = new Map;
74
- async findTasksByModel(model) {
75
- if (typeof model != "string")
49
+ async findTasksByModel(model_id) {
50
+ if (typeof model_id != "string")
76
51
  return;
77
- const junctions = await this.task2ModelTabularRepository.search({ model });
78
- if (!junctions || junctions.length === 0)
52
+ const modelRecord = await this.modelTabularRepository.get({ model_id });
53
+ if (!modelRecord)
79
54
  return;
80
- return junctions.map((junction) => junction.task);
55
+ return modelRecord.tasks && modelRecord.tasks.length > 0 ? modelRecord.tasks : undefined;
81
56
  }
82
57
  async enumerateAllTasks() {
83
- const junctions = await this.task2ModelTabularRepository.getAll();
84
- if (!junctions || junctions.length === 0)
58
+ const allModels = await this.modelTabularRepository.getAll();
59
+ if (!allModels || allModels.length === 0)
85
60
  return;
86
- const uniqueTasks = [...new Set(junctions.map((junction) => junction.task))];
87
- return uniqueTasks;
61
+ const uniqueTasks = new Set;
62
+ for (const model of allModels) {
63
+ if (model.tasks) {
64
+ for (const task of model.tasks) {
65
+ uniqueTasks.add(task);
66
+ }
67
+ }
68
+ }
69
+ return uniqueTasks.size > 0 ? Array.from(uniqueTasks) : undefined;
88
70
  }
89
71
  async enumerateAllModels() {
90
72
  const models = await this.modelTabularRepository.getAll();
91
73
  if (!models || models.length === 0)
92
74
  return;
93
- const parsedModels = models.map((model) => JSON.parse(model.details));
94
- parsedModels.forEach((m) => this.models.set(m.name, m));
95
- return parsedModels;
96
- }
97
- async connectTaskToModel(task, model) {
98
- await this.task2ModelTabularRepository.put({ task, model });
99
- this.events.emit("task_model_connected", task, model);
75
+ return models;
100
76
  }
101
- async findByName(name) {
102
- if (typeof name != "string")
77
+ async findByName(model_id) {
78
+ if (typeof model_id != "string")
103
79
  return;
104
- const modelstr = await this.modelTabularRepository.get({ name });
105
- if (!modelstr)
106
- return;
107
- const model = JSON.parse(modelstr.details);
108
- this.models.set(model.name, model);
109
- return model;
80
+ const model = await this.modelTabularRepository.get({ model_id });
81
+ return model ?? undefined;
110
82
  }
111
83
  async size() {
112
84
  return await this.modelTabularRepository.size();
113
85
  }
114
- async clear() {
115
- await this.modelTabularRepository.deleteAll();
116
- }
117
86
  }
118
87
 
119
- // src/model/storage/InMemoryModelRepository.ts
88
+ // src/model/ModelSchema.ts
89
+ var ModelSchema = {
90
+ type: "object",
91
+ properties: {
92
+ model_id: { type: "string" },
93
+ tasks: { type: "array", items: { type: "string" } },
94
+ title: { type: "string" },
95
+ description: { type: "string" },
96
+ provider: { type: "string" },
97
+ providerConfig: { type: "object", default: {} },
98
+ metadata: { type: "object", default: {} }
99
+ },
100
+ required: ["model_id", "tasks", "provider", "title", "description", "providerConfig", "metadata"],
101
+ additionalProperties: false
102
+ };
103
+ var ModelPrimaryKeyNames = ["model_id"];
104
+
105
+ // src/model/InMemoryModelRepository.ts
120
106
  class InMemoryModelRepository extends ModelRepository {
121
107
  modelTabularRepository;
122
- task2ModelTabularRepository;
123
108
  type = "InMemoryModelRepository";
124
109
  constructor() {
125
110
  super();
126
111
  this.modelTabularRepository = new InMemoryTabularRepository(ModelSchema, ModelPrimaryKeyNames);
127
- this.task2ModelTabularRepository = new InMemoryTabularRepository(Task2ModelSchema, Task2ModelPrimaryKeyNames, ["model"]);
128
112
  }
129
113
  }
130
114
 
131
115
  // src/model/ModelRegistry.ts
132
- class FallbackModelRegistry extends InMemoryModelRepository {
116
+ var MODEL_REPOSITORY = createServiceToken("model.repository");
117
+ if (!globalServiceRegistry.has(MODEL_REPOSITORY)) {
118
+ globalServiceRegistry.register(MODEL_REPOSITORY, () => new InMemoryModelRepository, true);
133
119
  }
134
- var modelRegistry;
135
120
  function getGlobalModelRepository() {
136
- if (!modelRegistry)
137
- modelRegistry = new FallbackModelRegistry;
138
- return modelRegistry;
121
+ return globalServiceRegistry.get(MODEL_REPOSITORY);
139
122
  }
140
123
  function setGlobalModelRepository(pr) {
141
- modelRegistry = pr;
124
+ globalServiceRegistry.registerInstance(MODEL_REPOSITORY, pr);
142
125
  }
143
126
 
144
127
  // src/provider/AiProviderRegistry.ts
145
- import { globalServiceRegistry, WORKER_MANAGER } from "@workglow/util";
128
+ import { globalServiceRegistry as globalServiceRegistry2, WORKER_MANAGER } from "@workglow/util";
146
129
 
147
130
  class AiProviderRegistry {
148
131
  runFnRegistry = new Map;
@@ -154,7 +137,7 @@ class AiProviderRegistry {
154
137
  }
155
138
  registerAsWorkerRunFn(modelProvider, taskType) {
156
139
  const workerFn = async (input, model, update_progress, signal) => {
157
- const workerManager = globalServiceRegistry.get(WORKER_MANAGER);
140
+ const workerManager = globalServiceRegistry2.get(WORKER_MANAGER);
158
141
  const result = await workerManager.callWorkerFunction(modelProvider, taskType, [input, model], {
159
142
  signal,
160
143
  onProgress: update_progress
@@ -475,25 +458,29 @@ class AiTask extends JobQueueTask {
475
458
  config.name ||= `${new.target.type || new.target.name}${input.model ? " with model " + input.model : ""}`;
476
459
  super(input, config);
477
460
  }
478
- async createJob(input, queue) {
461
+ async getJobInput(input) {
479
462
  if (typeof input.model !== "string") {
480
463
  console.error("AiTask: Model is not a string", input);
481
464
  throw new TaskConfigurationError("AiTask: Model is not a string, only create job for single model tasks");
482
465
  }
483
466
  const runtype = this.constructor.runtype ?? this.constructor.type;
484
467
  const model = await this.getModelForInput(input);
485
- const queueName = queue?.queueName ?? await this.getDefaultQueueName(input);
486
- if (!queueName) {
468
+ return {
469
+ taskType: runtype,
470
+ aiProvider: model.provider,
471
+ taskInput: input
472
+ };
473
+ }
474
+ async createJob(input, queueName) {
475
+ const jobInput = await this.getJobInput(input);
476
+ const resolvedQueueName = queueName ?? await this.getDefaultQueueName(input);
477
+ if (!resolvedQueueName) {
487
478
  throw new TaskConfigurationError("JobQueueTask: Unable to determine queue for AI provider");
488
479
  }
489
480
  const job = new AiJob({
490
- queueName,
481
+ queueName: resolvedQueueName,
491
482
  jobRunId: this.config.runnerId,
492
- input: {
493
- taskType: runtype,
494
- aiProvider: model.provider,
495
- taskInput: input
496
- }
483
+ input: jobInput
497
484
  });
498
485
  return job;
499
486
  }
@@ -532,7 +519,7 @@ class AiTask extends JobQueueTask {
532
519
  for (const [key, propSchema] of modelTaskProperties) {
533
520
  let requestedModels = Array.isArray(input[key]) ? input[key] : [input[key]];
534
521
  for (const model of requestedModels) {
535
- const foundModel = taskModels?.find((m) => m.name === model);
522
+ const foundModel = taskModels?.find((m) => m.model_id === model);
536
523
  if (!foundModel) {
537
524
  throw new TaskConfigurationError(`AiTask: Missing model for '${key}' named '${model}' for task '${this.type}'`);
538
525
  }
@@ -566,7 +553,7 @@ class AiTask extends JobQueueTask {
566
553
  const taskModels = await getGlobalModelRepository().findModelsByTask(this.type);
567
554
  for (const [key, propSchema] of modelTaskProperties) {
568
555
  let requestedModels = Array.isArray(input[key]) ? input[key] : [input[key]];
569
- let usingModels = requestedModels.filter((model) => taskModels?.find((m) => m.name === model));
556
+ let usingModels = requestedModels.filter((model) => taskModels?.find((m) => m.model_id === model));
570
557
  usingModels = usingModels.length > 1 ? usingModels : usingModels[0];
571
558
  input[key] = usingModels;
572
559
  }
@@ -663,7 +650,7 @@ var TypeLanguage = (annotations = {}) => ({
663
650
  minLength: 2,
664
651
  ...annotations
665
652
  });
666
- function TypeModel(semantic = "model", options = {}) {
653
+ function TypeModelAsString(semantic = "model", options = {}) {
667
654
  if (semantic !== "model" && !semantic.startsWith("model:")) {
668
655
  throw new Error("Invalid semantic value");
669
656
  }
@@ -676,6 +663,11 @@ function TypeModel(semantic = "model", options = {}) {
676
663
  type: "string"
677
664
  };
678
665
  }
666
+ function TypeModel(semantic = "model", options = {}) {
667
+ return {
668
+ oneOf: [TypeModelAsString(semantic, options), ModelSchema]
669
+ };
670
+ }
679
671
  var TypeReplicateArray = (type, annotations = {}) => ({
680
672
  oneOf: [type, { type: "array", items: type }],
681
673
  title: type.title,
@@ -1117,11 +1109,15 @@ var TextTranslationInputSchema = {
1117
1109
  }),
1118
1110
  source_lang: TypeReplicateArray(TypeLanguage({
1119
1111
  title: "Source Language",
1120
- description: "The source language"
1112
+ description: "The source language",
1113
+ minLength: 2,
1114
+ maxLength: 2
1121
1115
  })),
1122
1116
  target_lang: TypeReplicateArray(TypeLanguage({
1123
1117
  title: "Target Language",
1124
- description: "The target language"
1118
+ description: "The target language",
1119
+ minLength: 2,
1120
+ maxLength: 2
1125
1121
  })),
1126
1122
  model: modelSchema7
1127
1123
  },
@@ -1138,7 +1134,9 @@ var TextTranslationOutputSchema = {
1138
1134
  },
1139
1135
  target_lang: TypeLanguage({
1140
1136
  title: "Output Language",
1141
- description: "The output language"
1137
+ description: "The output language",
1138
+ minLength: 2,
1139
+ maxLength: 2
1142
1140
  })
1143
1141
  },
1144
1142
  required: ["text", "target_lang"],
@@ -1293,18 +1291,6 @@ function normalize(vector) {
1293
1291
  }
1294
1292
  return new Float32Array(normalized);
1295
1293
  }
1296
- // src/model/storage/IndexedDbModelRepository.ts
1297
- import { IndexedDbTabularRepository } from "@workglow/storage";
1298
- class IndexedDbModelRepository extends ModelRepository {
1299
- modelTabularRepository;
1300
- task2ModelTabularRepository;
1301
- type = "IndexedDbModelRepository";
1302
- constructor(tableModels = "models", tableTask2Models = "task2models") {
1303
- super();
1304
- this.modelTabularRepository = new IndexedDbTabularRepository(tableModels, ModelSchema, ModelPrimaryKeyNames);
1305
- this.task2ModelTabularRepository = new IndexedDbTabularRepository(tableTask2Models, Task2ModelSchema, Task2ModelPrimaryKeyNames, ["model"]);
1306
- }
1307
- }
1308
1294
  export {
1309
1295
  setGlobalModelRepository,
1310
1296
  setAiProviderRegistry,
@@ -1316,6 +1302,7 @@ export {
1316
1302
  VectorSimilarityTask,
1317
1303
  TypedArraySchema,
1318
1304
  TypeReplicateArray,
1305
+ TypeModelAsString,
1319
1306
  TypeModel,
1320
1307
  TypeLanguage,
1321
1308
  TextTranslationTask,
@@ -1343,15 +1330,13 @@ export {
1343
1330
  TextEmbeddingOutputSchema,
1344
1331
  TextEmbeddingInputSchema,
1345
1332
  TextEmbedding,
1346
- Task2ModelSchema,
1347
- Task2ModelPrimaryKeyNames,
1348
1333
  TableFragment,
1349
1334
  SimilarityFn,
1350
1335
  Similarity,
1351
1336
  ModelSchema,
1352
1337
  ModelRepository,
1353
1338
  ModelPrimaryKeyNames,
1354
- IndexedDbModelRepository,
1339
+ MODEL_REPOSITORY,
1355
1340
  InMemoryModelRepository,
1356
1341
  ImageFragment,
1357
1342
  DownloadModelTask,
@@ -1368,4 +1353,4 @@ export {
1368
1353
  AiJob
1369
1354
  };
1370
1355
 
1371
- //# debugId=CB10B0DB935086A564756E2164756E21
1356
+ //# debugId=55D3E7D54413645E64756E2164756E21